package com.coderman.topn;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.io.FileUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * @Author fanchunshuai
 * @Date 2019/11/23 18
 * @Description: https://www.jianshu.com/p/f092fb562c87
 * <p>
 * 蔚来汽车面试题 千万数据TopN
 * 题目:随机生成 Salary {name, baseSalary, bonus }的记录，如“wxxx,10,1”，每行一条记录，总共1000万记录，写入文本文件（UFT-8编码），
 * 然后读取文件，name的前两个字符相同的，其年薪累加，比如wx，100万，3个人，最后做排序和分组，输出年薪总额最高的10组：
 * wx, 200万，10人
 * lt, 180万，8人
 * ....
 * name 4位a-z随机， baseSalary [0,100]随机 bonus[0-5]随机 年薪总额 = baseSalary*13 + bonus
 * <p>
 * 请努力将程序优化到5秒内执行完
 * <p>
 * 要求生成随机数据格式:{"baseSalary":26,"bonus":4,"name":"lvox"}
 * <p>
 * 作者：heoi836
 * 链接：https://www.jianshu.com/p/f092fb562c87
 * 来源：简书
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 */
public class TopNSalary {
    public static void main(String[] args) {
       /* long startTime = System.currentTimeMillis();
        List<String> salaryList = new ArrayList<>(10000000);
        List listProxy = Collections.synchronizedList(salaryList);
        Random random = new Random();
        for (int i = 0; i < 5000000; i++) {
            //这里不建议用Salary对象进行数据构造，构造避免耗时过长
            String name = getName();
            int salary = random.nextInt(100);
            int bonus = random.nextInt(5);
            String x = "{\"baseSalary\":" + salary + ",\"bonus\":" + bonus + ",\"name\":\"" + name + "\"}";
            listProxy.add(x);
        }*/

        //long endTime = System.currentTimeMillis();
        //System.out.println("endTime - startTime = " + (endTime - startTime));
        //String path = "D:\\tmp\\topn\\topnall\\allsalary\\allsalarya.txt";
       /*
       第一种方式生成文件
       try {
            FileUtils.writeLines(new File(path),listProxy,"\n");
        } catch (IOException e) {
            e.printStackTrace();
        }*/

        String path2 = "D:\\tmp\\topn\\topnall\\allsalary\\allsalaryb.txt";
        /*try {
            FileWriter out = new FileWriter(new File(path2), true);
            Integer count = 10000000;
            while (true){
                count --;
                //这里不建议用Salary对象进行数据构造，构造避免耗时过长
                String name = getName();
                int salary = random.nextInt(100);
                int bonus = random.nextInt(5);
                String x = "{\"baseSalary\":"+salary+",\"bonus\":"+bonus+",\"name\":\""+name+"\"}";
                out.write(x);
                out.write("\n");
                if (count ==0) {
                    out.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        long endTime3 = System.currentTimeMillis();
        System.out.println("endTime3 - endTime2 = "+(endTime3 - endTime2));

*/
        try {








            /**
             * 其实也可以用随机流;将文件分块;多起几个线程执行;
             这样的做的话得将文件分块;意思就是通过scan的api;先全文循环后标记;分成几份.
             1000w的数据不大.这样反而效率更低
             */
            long endTime2 = System.currentTimeMillis();
            BufferedReader reader = new BufferedReader(new FileReader(path2));
            String line = null;
            HashMap<String,Salary> map = new HashMap(500);
            while((line = reader.readLine()) != null){
                Salary salary = JSON.parseObject(line, Salary.class);
                String key = salary.getName().substring(0, 2);
                Salary result = map.get(key);
                if (result != null) {
                    result.setBaseSalary(result.getBaseSalary() + salary.getBaseSalary() * 13 + salary.getBonus());
                    result.setBonus(result.getBonus() +1);
                }else {
                    result = new Salary();
                    result.setName(key);
                    result.setBonus(1);
                    result.setBaseSalary(salary.getBaseSalary() * 13 + salary.getBonus());
                    map.put(key,result);
                }
            }
            System.out.println(map.size()+"==========");

            ArrayList<Salary> values = new ArrayList();
            Collection<Salary> co = map.values();
            values.addAll(co);
            List<Salary> list = map.values().stream().sorted((o1, o2) -> o2.getBaseSalary() - o1.getBaseSalary()).collect(Collectors.toList());
            long endTime3 = System.currentTimeMillis();

            System.out.println("endTime3 - endTime2 = "+(endTime3 -endTime2)+"ms");
            for (int i = 0; i < 10; i++) {
                System.out.println(JSON.toJSONString(list.get(i)));
            }


            /*long startTime2 = System.currentTimeMillis();
            List<String> salaryJSONList = FileUtils.readLines(new File(path2), "UTF-8");
            long endTime2 = System.currentTimeMillis();
            System.out.println("endTime2 - startTime2 = "+(endTime2 -startTime2)+"ms");

            Map<String, Salary> map = new HashMap<>();
            for (String str : salaryJSONList) {
                Salary salary = JSON.parseObject(str, Salary.class);
                String key = salary.getName().substring(0, 2);
                Salary result = map.get(key);
                if (result != null) {
                    result.setBaseSalary(result.getBaseSalary() + salary.getBaseSalary() * 13 + salary.getBonus());
                    result.setBonus(result.getBonus() + 1);
                } else {
                    result = new Salary();
                    result.setName(key);
                    result.setBonus(1);
                    result.setBaseSalary(salary.getBaseSalary() * 13 + salary.getBonus());

                    map.put(key, result);
                }
            }
            ArrayList<Salary> values = new ArrayList();

            Collection<Salary> co = map.values();
            values.addAll(co);
            *//**
             java8之后提供流排序;效率更高
             **//*
            List<Salary> list = map.values().stream().sorted((o1, o2) -> o2.getBaseSalary() - o1.getBaseSalary()).collect(Collectors.toList());

       *//* Collections.sort( values, new Comparator<Salary>() {
            public int compare(Salary o1, Salary o2) {
                return o2.getBaseSalary() - o1.getBaseSalary();
            }
        });*//*
            long endTime3 = System.currentTimeMillis();

            System.out.println("endTime3 - endTime2 = "+(endTime3 -endTime2)+"ms");
            for (int i = 0; i < 10; i++) {
                System.out.println(JSON.toJSONString(list.get(i)));
            }
*/
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    private static String getName() {
        String chars = "abcdefghijklmnopqrstuvwxyz";
        StringBuilder name = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            name.append(chars.charAt((int) (Math.random() * 26)));
        }
        return name.toString();
    }
}

class Salary {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年薪
     */
    private int baseSalary;
    /**
     * 奖金
     */
    private int bonus;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getBaseSalary() {
        return baseSalary;
    }

    public void setBaseSalary(int baseSalary) {
        this.baseSalary = baseSalary;
    }

    public int getBonus() {
        return bonus;
    }

    public void setBonus(int bonus) {
        this.bonus = bonus;
    }

    @Override
    public String toString() {
        return name + "," + baseSalary + "," + bonus;
    }
}




